外部活动导入接口 (External Event Import)
接口概述
该接口用于从第三方活动平台导入活动数据到 Pear 系统。支持多个主流活动平台的导入,包括 Eventbrite、Poshvip、Raco、Shotgun 和 DiceFM。
[!important] 架构说明 本接口为 Admin Server 代理接口,实际业务逻辑由下游 Pear API 服务处理。Admin Server 负责请求转发和认证。
请求信息
基本参数
| 属性 | 值 |
|---|---|
| 请求地址 | /users/tool/external-event-import/:userId |
| 请求方式 | POST |
| Content-Type | application/json |
| 认证方式 | JWT Bearer Token |
路径参数 (Path Parameters)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
userId |
string (UUID) |
✅ | 目标用户 ID,活动将导入到该用户账户下 |
请求头 (Headers)
| Header | 类型 | 必填 | 说明 |
|---|---|---|---|
Authorization |
string |
✅ | JWT 认证令牌,格式:Bearer <token> |
Content-Type |
string |
✅ | 固定值:application/json |
timezone |
string |
⚠️ | 用户时区,如 Asia/Shanghai。部分平台解析需要时区信息 |
x-track-id |
string (UUID) |
❌ | 请求追踪 ID,用于日志关联 |
accept |
string |
❌ | 接受的响应类型,如 application/json |
[!warning] 时区重要性
timezone头信息会被转发到下游服务。某些活动平台(如 Poshvip)的时间解析依赖此时区信息,缺失可能导致Failed to fetch timeZone错误。
请求体 (Request Body)
请求结构
interface EventImportRequest {
type: EventImportType; // 活动来源平台类型
url: string; // 活动页面 URL
}
字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
enum |
✅ | 活动来源平台类型,见下方枚举值 |
url |
string |
✅ | 第三方平台的完整活动 URL |
type 枚举值
| 值 | 平台 | URL 示例 |
|---|---|---|
eventbrite |
Eventbrite | https://www.eventbrite.com/e/xxx-tickets-123456789 |
poshvip |
Poshvip | https://posh.vip/e/xxx |
raco |
Raco | - |
shotgun |
Shotgun | - |
dicefm |
DiceFM | - |
请求示例
{
"type": "eventbrite",
"url": "https://www.eventbrite.com/e/the-association-cocktail-classes-tickets-164264039163"
}
{
"type": "poshvip",
"url": "https://posh.vip/e/example-event"
}
响应信息
成功响应
[!note] 响应说明 实际响应结构由下游 Pear API 服务决定,Admin Server 透传下游响应的
data字段。
interface SuccessResponse {
// 响应结构由下游服务定义
// 通常包含导入的活动信息
}
成功响应示例
{
"id": "event-uuid-here",
"title": "The Association Cocktail Classes",
"description": "Event description...",
"startTime": "2026-03-15T19:00:00Z",
"endTime": "2026-03-15T22:00:00Z",
"location": {
"name": "Venue Name",
"address": "123 Street, City"
},
// ... 其他活动字段
}
错误响应
错误响应结构
interface ErrorResponse {
code: number; // HTTP 状态码
message: string; // 错误信息
data?: any; // 附加错误详情(可选)
}
常见错误码
| HTTP 状态码 | 错误场景 | 说明 |
|---|---|---|
400 |
参数验证失败 | type 不是有效的枚举值,或 url 格式不正确 |
401 |
认证失败 | JWT Token 无效或已过期 |
404 |
用户不存在 | 指定的 userId 在系统中不存在 |
409 |
业务冲突 | 活动已存在或其他业务冲突 |
500 |
服务器内部错误 | 下游服务异常、网络超时等 |
错误响应示例
参数验证失败 (400)
{
"code": 400,
"message": "type must be one of the following values: eventbrite, poshvip, raco, shotgun, dicefm",
"data": null
}
时区获取失败 (500)
{
"code": 500,
"message": "Failed to fetch timeZone",
"data": {
"platform": "poshvip",
"url": "https://posh.vip/e/xxx"
}
}
活动 URL 无效
{
"code": 400,
"message": "Invalid event URL format",
"data": {
"url": "invalid-url"
}
}
完整请求示例
cURL 示例
curl -X POST 'https://admin.katana-api.1m.app/users/tool/external-event-import/ad57de31-bf92-413b-ae4d-8609b5ff0680' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
-H 'timezone: Asia/Shanghai' \
-H 'x-track-id: ac025c47-a2de-4634-8067-8642e14a24c4' \
-d '{
"type": "eventbrite",
"url": "https://www.eventbrite.com/e/the-association-cocktail-classes-tickets-164264039163"
}'
JavaScript (fetch) 示例
const response = await fetch(
'https://admin.katana-api.1m.app/users/tool/external-event-import/ad57de31-bf92-413b-ae4d-8609b5ff0680',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'timezone': 'Asia/Shanghai',
'x-track-id': crypto.randomUUID()
},
body: JSON.stringify({
type: 'eventbrite',
url: 'https://www.eventbrite.com/e/the-association-cocktail-classes-tickets-164264039163'
})
}
);
const result = await response.json();
下游服务请求 (Pear API)
Admin Server 通过 RPC 调用下游 Pear API 服务,以下是转发请求的详细参数。
[!tip] 内部服务调用 此部分为 Admin Server 与 Pear API 之间的内部通信,供后端开发参考。
请求信息
| 属性 | 值 |
|---|---|
| Base URL | {KATANA_URL} (环境变量配置) |
| 请求路径 | /external-event-import/admin/:userId |
| 请求方式 | POST |
| Content-Type | application/json |
请求头 (Headers)
Admin Server 会自动添加以下认证头:
| Header | 来源 | 说明 |
|---|---|---|
Pear-Client-Id |
环境变量 PEAR_CLIENT_ID |
服务间认证标识 |
Pear-Client-Secret |
环境变量 PEAR_CLIENT_SECRET |
服务间认证密钥 |
[!note] 头信息传递 客户端传递的
timezone、x-track-id等头信息也会被转发到下游服务。
请求体 (Body)
请求体直接透传,与客户端请求体一致:
{
"type": "eventbrite",
"url": "https://www.eventbrite.com/e/xxx-tickets-123456789"
}
完整请求示例
# Admin Server -> Pear API 内部调用
curl -X POST '${KATANA_URL}/external-event-import/admin/ad57de31-bf92-413b-ae4d-8609b5ff0680' \
-H 'Content-Type: application/json' \
-H 'Pear-Client-Id: ${PEAR_CLIENT_ID}' \
-H 'Pear-Client-Secret: ${PEAR_CLIENT_SECRET}' \
-H 'timezone: Asia/Shanghai' \
-d '{
"type": "eventbrite",
"url": "https://www.eventbrite.com/e/the-association-cocktail-classes-tickets-164264039163"
}'
响应处理
下游 Pear API 返回格式:
interface PearApiResponse<T> {
data: T; // Admin Server 提取此字段返回给客户端
}
Admin Server 提取 response.data.data 作为最终响应返回给客户端。
RPC Service 实现
// src/rpc/rpc.service.ts
async requestToPear<T = any>(
url: string,
config?: Parameters<typeof axios>[1],
): Promise<T> {
try {
const result = await axios(url, {
baseURL: this.configService.get<string>('KATANA_URL'),
...config,
headers: {
['Pear-Client-Id']: this.configService.get('PEAR_CLIENT_ID'),
['Pear-Client-Secret']: this.configService.get('PEAR_CLIENT_SECRET'),
...config?.headers, // 合并额外的 headers
},
});
return result.data.data as T; // 提取 data.data 返回
} catch (error: any) {
throw new CustomException(
error?.response?.status ?? 409,
error?.response?.data?.message,
error?.response?.data?.data,
);
}
}
业务逻辑
处理流程
participant Client as 客户端
participant Admin as Admin Server
participant Pear as Pear API (下游服务)
participant Platform as 第三方平台
Client->>Admin: POST /external-event-import/:userId
Note over Client,Admin: Headers: Authorization, timezone
Note over Client,Admin: Body: {type, url}
Admin->>Admin: JWT 认证验证
Admin->>Admin: 参数验证 (class-validator)
Admin->>Pear: POST /external-event-import/admin/:userId
Note over Admin,Pear: 转发请求体和 Headers
Pear->>Platform: 爬取/解析活动数据
Platform-->>Pear: 返回活动信息
Pear->>Pear: 创建活动记录
Pear-->>Admin: 返回活动数据
Admin-->>Client: 透传响应数据
架构说明
[!info] 代理模式 Admin Server 采用代理模式,核心职责包括:
- 认证鉴权:验证 JWT Token 有效性
- 参数校验:使用
class-validator验证请求参数- 请求转发:通过 RPC 调用下游 Pear API 服务
- 错误处理:捕获下游异常并转换为统一错误格式
关键代码位置
| 组件 | 文件路径 | 说明 |
|---|---|---|
| Controller | src/users/users.tool.controller.ts:85-97 |
端点定义和请求处理 |
| DTO | src/users/user.interface.ts:190-196 |
EventImportRequest 类型定义 |
| Enum | src/users/user.interface.ts:182-188 |
EventImportType 枚举定义 |
| RPC Service | src/rpc/rpc.service.ts |
下游服务调用 |
| Exception Filter | src/filter/any.exception.filter.ts |
全局异常处理 |
注意事项
必读事项
[!danger] 认证要求
- 所有请求必须携带有效的 JWT Bearer Token
- Token 通过
Authorization头传递- 未认证请求将返回
401 Unauthorized[!warning] 时区设置
- 强烈建议始终传递
timezone头- 格式为 IANA 时区标识符,如
Asia/Shanghai、America/New_York- 缺失可能导致部分平台导入失败
[!caution] URL 有效性
- 确保 URL 为目标平台的有效活动页面
- URL 必须可公开访问(无需登录即可查看)
- 私有或已下线的活动可能无法导入
平台特定说明
| 平台 | 特殊要求 | 已知限制 |
|---|---|---|
eventbrite |
URL 必须包含 /e/ 路径 |
- |
poshvip |
必须传递 timezone 头 | 可能出现 timeZone 获取失败 |
raco |
- | - |
shotgun |
- | - |
dicefm |
- | - |
错误排查
| 错误信息 | 可能原因 | 解决方案 |
|---|---|---|
Failed to fetch timeZone |
Poshvip 平台缺少 timezone 头 | 添加 timezone: Asia/Shanghai 头 |
type must be one of... |
type 参数值无效 | 检查 type 是否为支持的枚举值 |
Requested resource not found |
用户 ID 不存在 | 验证 userId 是否正确 |
Network timeout |
下游服务响应超时 | 重试请求或检查服务状态 |
相关接口
- [[kat-music-widget-import-音乐组件导入]] - 音乐组件导入接口
- [[kat-external-user-import-外部用户导入]] - Partner/Promoter 用户导入
- [[kat-web-scraper-网页爬虫]] - Komi/Linktr 爬虫接口
变更历史
| 日期 | 版本 | 变更内容 |
|---|---|---|
| 2026-03-03 | 1.0 | 初始版本,梳理完整接口文档 |
附录
EventImportRequest 类型定义
// src/users/user.interface.ts
export enum EventImportType {
EVENTBRITE = 'eventbrite',
POSHVIP = 'poshvip',
RACO = 'raco',
SHOTGUN = 'shotgun',
DICEFM = 'dicefm',
}
export class EventImportRequest {
@IsEnum(EventImportType)
type: EventImportType;
@IsString()
url: string;
}
Controller 实现
// src/users/users.tool.controller.ts
@Post('external-event-import/:userId')
async importEvent(
@Param('userId') userId: string,
@Body() body: EventImportRequest,
) {
return this.rpcService.requestToPear(
`external-event-import/admin/${userId}`,
{
method: 'POST',
data: body,
},
);
}